import json
import logging
import re
import traceback
from json import JSONDecodeError
from typing import List, Any, Dict

import jsonpath

from common.py_script import py_script
from system.models import *
import requests

from utils.form_data_utils import post_assemble_form_data

logging = logging.getLogger("log")


class Header:
    def __init__(self, key: str, value: str):
        self.key = key
        self.value = value

    # 假设这是getStringByParameterExtraction的Python实现


# GetExtractions类的Python等价物
class GetExtractions:
    def __init__(self, extract_name: str, expression: str, value: str, is_success: bool):
        self.extract_name = extract_name
        self.expression = expression
        self.value = value
        self.is_success = is_success

    @classmethod
    def from_string(cls, str_dict):
        # 解析JSON字符串
        extract_name = str_dict["extract_name"]  # 示例值
        expression = str_dict["expression"]  # 示例值
        is_success = str_dict["is_success"]  # 假设总是成功，或根据某些条件更改它
        value = str_dict['value']

        # 使用解析出的值和硬编码的值来创建实例
        return cls(extract_name, expression, value, is_success)

    def to_dict(self):
        return {
            "extract_name": self.extract_name,
            "expression": self.expression,
            "value": self.value,
            "is_success": self.is_success
        }

    def __str__(self):
        return json.dumps(self.to_dict(), indent=4)


class Params:
    def __init__(self, key: str, value: str):
        self.key = key
        self.value = value


def execute_api_case(api_info: dict, get_extractions_list: List[GetExtractions]) -> dict:
    """
    requests 核心方法调用，及逻辑编写
    :param get_extractions_list:
    :return:
    :param api_info:
    :return:
    """
    url = get_string_by_parameter_extraction_else(api_info["domain"], api_info["project_id"], api_info["env_id"])
    url += api_info["path"]
    api_request_result = {
        "project_id", "api_id", "api_name", "api_test_case_id", "api_test_case_step_id", "task_log_id", "method", "url",
        "request_header", "request_param_type", "request_param", "request_body", "request_data_params", "result_body",
        "response_headers", "result_status", "result_time", "result_assert", "result_is_pass", "result_extractions",
        "exception_body", "is_deleted"
    }

    api_info_model = api_init(api_info, get_extractions_list)
    api_info["request_header"] = api_info_model["request_header"]
    api_info["request_param"] = api_info_model["request_param"]
    api_info["request_data_params"] = api_info_model["request_data_params"]
    api_info["request_body"] = api_info_model["request_body"]
    logging.info(
        f"替换参数列表:{json.dumps([extractions.to_dict() for extractions in get_extractions_list], indent=4)}")
    logging.info("执行用例的api入参：{}".format(api_info))
    # 将api_info中原有数据复制到api_request_result中
    api_request_result = {k: v for k, v in api_info.items() if k in api_request_result}
    api_request_result["api_id"] = api_info["id"]
    api_request_result["api_name"] = api_info["name"]
    api_request_result["url"] = url
    headers = get_req_header_list(api_info_model["request_header"], get_extractions_list)
    # 根据method 以及requestParamType来确认请求方式
    try:
        logging.info("====开始执行自动化接口用例====")
        method = api_info["method"].lower()
        request_param_type = api_info["request_param_type"].lower()
        request_param = select_request_params(api_info_model, get_extractions_list, method)
        if method == 'get':
            response = requests.get(url=url, params=request_param, headers=headers)
        elif method == 'post' and request_param_type == 'raw':
            response = requests.post(url=url, json=json.loads(api_info["request_body"]), headers=headers)
        elif method == 'post' and request_param_type == 'form':
            # form-data格式自己封装请求
            body, boundary = post_assemble_form_data(request_param)
            content_type = f'multipart/form-data; boundary={boundary}'
            headers["Content-Type"] = content_type
            response = requests.post(url=url, data=body, headers=headers)
        elif method == 'post' and request_param_type == 'x-www-form-urlencoded':
            response = requests.post(url=url, data=request_param, headers=headers)
        elif method == 'put':
            response = requests.put(url=url, data=request_param, headers=headers)
        elif method == 'delete':
            response = requests.delete(url=url, data=request_param, headers=headers)
        else:
            # 缺少很多请求方式 后续用到后再新增
            api_request_result["exception_body"] = "未查询到请求方式，请修改请求方式"
            logging.info(
                f"未查询到请求方式，请修改请求方式,method:{api_info['method'].lower()},request_param_type:{api_info['request_param_type'].lower()}")
            return api_request_result

    except Exception as e:
        traceback.print_exc()
        api_request_result["exception_body"] = str(e)
        logging.error(e)
        return api_request_result
    result_assert = get_result_assert(str(response.content, 'utf8'), api_info["request_assert"])
    # 请求结果内容写入，可查看api_request_result的模型
    api_request_result["result_body"] = str(response.content, 'utf8')
    api_request_result["result_status"] = response.status_code
    api_request_result["exception_body"] = "无异常"
    if api_request_result["result_status"] != 200:
        result_assert["result_is_pass"] = False
    api_request_result["response_headers"] = get_res_header_list(response)
    api_request_result["result_time"] = int(response.elapsed.total_seconds() * 1000)
    get_result_assert_result = get_parameter_extractions(str(response.content, 'utf8'),
                                                         api_info['parameter_extraction'],
                                                         get_res_header_list(response))
    api_request_result["result_assert"] = result_assert["request_assert_request_list"]
    api_request_result["result_is_pass"] = result_assert["result_is_pass"]
    api_request_result["result_extractions"] = get_result_assert_result
    logging.info("api执行完成，返回执行结果:{}".format(api_request_result))
    return api_request_result


# 定义一个函数来根据请求方法选择正确的参数
def select_request_params(api_info_model, get_extractions_list, method):
    if method.lower() == 'post':
        return get_req_params(api_info_model["request_data_params"], get_extractions_list)
    else:
        return get_req_params(api_info_model["request_param"], get_extractions_list)


def get_result_assert(res: str, request_assert: list) -> dict:
    """

    :param res:
    :param request_assert:
    :return:
    """
    request_assert_request_list = []
    # 接口测试结果默认通过，当存在一个校验失败，则展示失败
    result_is_pass = True
    if request_assert:
        for request_assert_item in request_assert:
            # api测试结果json固定格式
            result_assert = {
                "check_list": request_assert_item['check_list'],
                "value": request_assert_item['value'],
                'real_value': '',
                'result': False
            }
            try:
                real_value = jsonpath.jsonpath(json.loads(res), '$.' + request_assert_item['check_list'])
                if real_value:
                    result_assert['real_value'] = str(real_value[0])
                    if str(real_value[0]) == request_assert_item['value']:
                        result_assert['result'] = True
                    else:
                        result_is_pass = False
                else:
                    result_assert['real_value'] = None
            except json.JSONDecodeError as e:
                logging.error("处理请求体时发生错误：", exc_info=e)
                result_is_pass = False
                result_assert['real_value'] = None

            # 多个测试结果列表存储
            request_assert_request_list.append(result_assert)
    result_assert = {
        "request_assert_request_list": json.dumps(request_assert_request_list, ensure_ascii=False),
        "result_is_pass": result_is_pass
    }
    return result_assert


def get_parameter_extractions(res: str, parameter_extraction_list, header_list):
    """
    获取提取参数内容
    :param header_list:
    :param res:
    :param parameter_extraction_list:
    :return:
    """
    get_extractions_list = []
    if parameter_extraction_list:
        for parameter_extraction in parameter_extraction_list:
            if parameter_extraction["data_type"] == 1:
                try:
                    get_parameter_extraction = jsonpath.jsonpath(json.loads(res),
                                                                 '$.' + parameter_extraction['expression'])[0]
                    get_extractions = GetExtractions(parameter_extraction['extract_name'],
                                                     parameter_extraction['expression'], get_parameter_extraction, True)
                    get_extractions_list.append(get_extractions.__dict__)
                except (IndexError, KeyError, TypeError, AttributeError, JSONDecodeError) as e:
                    get_extractions = GetExtractions(parameter_extraction['extract_name'],
                                                     parameter_extraction['expression'], "解析失败，未找到表达式内容",
                                                     False)
                    get_extractions_list.append(get_extractions.__dict__)
            elif parameter_extraction["data_type"] == 0:
                for header in json.loads(header_list):
                    if parameter_extraction['expression'] == header["key"]:
                        get_extractions = GetExtractions(parameter_extraction['extract_name'],
                                                         parameter_extraction['expression'],
                                                         header["value"],
                                                         True)
                        get_extractions_list.append(get_extractions.__dict__)
        logging.info(f"返回提取参数内容：{get_extractions_list}")
        return json.dumps(get_extractions_list)
    else:
        return json.dumps(get_extractions_list)


def get_res_header_list(resp: requests.Response):
    """
    获取返回头列表
    :param resp:
    :return:
    """
    headers = resp.headers
    # 将头部信息转换为列表，每个元素为一个键值对
    header_dict = dict(headers)
    header_list = [{"key": key, "value": value} for key, value in header_dict.items()]
    return json.dumps(header_list)


def get_request_body(request_body: str, get_extractions_list: List[GetExtractions]) -> str:
    """
    :param request_body:
    :param get_extractions_list:
    """

    try:
        request_body_dict = json.loads(request_body)
        for key, value in request_body_dict.items():
            request_body_dict[key] = get_string_by_parameter_extraction(value, get_extractions_list)
        request_body = json.dumps(request_body_dict)
        return request_body
    except json.JSONDecodeError as e:
        logging.error("处理请求体时发生错误：", exc_info=e)
        return request_body


def get_string_by_parameter_extraction(str_value: str, get_extractions_list: List[GetExtractions]) -> str:
    """

    :param str_value:
    :param get_extractions_list:
    :return:
    """
    # 判断str字段是否存在$字段
    if isinstance(str_value, str) and str_value and '$' in str_value:
        logging.info("-----进入提取接口---")
        # 正则表达式
        pattern = re.compile(r"\$\{(.*?)\}")
        matches = pattern.finditer(str_value)
        for match in matches:
            find_string = match.group(1)
            old_string = "${" + find_string + "}"
            # 循环获取GetExtractions对象
            for get_extractions in get_extractions_list:
                # 判断正则表达式获取结果是否与参数一致，一致则返回结果
                if find_string == get_extractions.extract_name and get_extractions.is_success:
                    new_string = str(get_extractions.value)
                    str_value = str_value.replace(old_string, new_string)
                    logging.info(
                        f"get_string_by_parameter_extraction方法 old_string:{old_string}, new_string:{new_string},str_value:{str_value}")
                    break  # 如果找到匹配项，则跳出循环
    else:
        # 判断是否存在自定义参数，python脚本实现
        str_value = py_script(str_value)
        logging.info(f"get_string_by_parameter_extraction方法 python脚本实现 返回修改后字符串: {str_value}")
    return str_value


def get_string_by_parameter_extraction_else(str_value: str, project_id, env_id):
    """
    通过@{}标识符参数化
    :param str_value:
    :param project_id:
    :param env_id:
    :return:
    """
    # 固定格式 正则表达式模式，匹配@{}中间的内容
    pattern = re.compile(r"\@\{(.*?)\}")
    matches = pattern.finditer(str_value)
    for match in matches:
        find_string = match.group(1)
        old_string = "@{" + find_string + "}"
        if EnvParams.objects.filter(name=find_string, project_id=project_id, is_deleted=0):
            env_params = EnvParams.objects.get(name=match.group(1), project_id=project_id,
                                               is_deleted=0).as_dict()
            for item in json.loads(env_params["value"]):
                if env_id == item["env_id"]:
                    new_string = item["env_value"]
                    str_value = str_value.replace(old_string, new_string)
                    logging.info(
                        f"get_string_by_parameter_extraction_else方法 old_string:{old_string}, new_string:{new_string},str_value:{str_value}")
                    break  # 如果找到匹配项，则跳出循环

    return str_value


def get_req_header_list(h_list: str, get_extractions_list: List[GetExtractions]) -> Dict[str, Any]:
    if not h_list:
        return {}
    headers = {}
    try:
        header_list = [Header(item['key'], item['value']) for item in json.loads(h_list)]
        for header in header_list:
            extracted_value = get_string_by_parameter_extraction(header.value, get_extractions_list)
            headers[header.key] = extracted_value
        return headers
    except Exception as e:
        logging.error("获取消息头报错：", e)

    return headers


def get_req_params(ps: List[Params], get_extractions_list: List[GetExtractions]) -> Dict[str, Any]:
    # 如果ps为空，则返回一个空字典
    if not ps:
        return {}

        # 创建一个字典来存储参数
    get_req_param_list = {}
    try:
        # 遍历Params列表
        params_objects = [Params(item['key'], item['value']) for item in json.loads(ps)]
        for params in params_objects:
            # 调用函数获取字符串，并添加到字典中
            get_req_param_list[params.key] = get_string_by_parameter_extraction(params.value, get_extractions_list)

        # 返回填充好的字典
        return get_req_param_list

    except Exception as e:
        # 如果发生异常，记录错误并返回空字典
        logging.error("获取参数报错：", exc_info=e)
        return get_req_param_list


def api_init(api_info, get_extractions_list: List[GetExtractions]):
    # 处理请求头
    headers = []
    if api_info["request_header"]:
        header_list = [Header(item['key'], item['value']) for item in api_info["request_header"]]
        for header in header_list:
            header.value = get_string_by_parameter_extraction(header.value, get_extractions_list)
            header.value = get_string_by_parameter_extraction_else(header.value, api_info["project_id"],
                                                                   api_info["env_id"])
            headers.append(header.__dict__)
    # 处理请求参数
    request_params = []
    if api_info["request_param"]:
        for request_param in api_info["request_param"]:
            request_param["value"] = get_string_by_parameter_extraction(request_param["value"], get_extractions_list)
            request_param["value"] = get_string_by_parameter_extraction_else(request_param["value"],
                                                                             api_info["project_id"], api_info["env_id"])
            request_params.append(request_param)
    # 处理请求数据参数
    request_data_params = []
    if api_info["request_data_params"]:
        for request_data_param in api_info["request_data_params"]:
            request_data_param["value"] = get_string_by_parameter_extraction(request_data_param["value"],
                                                                             get_extractions_list)
            request_data_param["value"] = get_string_by_parameter_extraction_else(request_data_param["value"],
                                                                                  api_info["project_id"],
                                                                                  api_info["env_id"])
            request_data_params.append(request_data_param)

        # 处理请求体（如果存在）
    if api_info["request_body"]:
        try:
            request_body_dict = json.loads(api_info["request_body"])
            if isinstance(request_body_dict, dict):
                for key, value in request_body_dict.items():
                    str_value = get_string_by_parameter_extraction(value, get_extractions_list)
                    request_body_dict[key] = str_value
                    request_body_dict[key] = get_string_by_parameter_extraction_else(str_value, api_info["project_id"],
                                                                                     api_info["env_id"])
                api_info["request_body"] = json.dumps(request_body_dict)
            else:
                request_body_list = []
                for request_body in request_body_dict:
                    for key, value in request_body.items():
                        request_body[key] = get_string_by_parameter_extraction(value, get_extractions_list)
                    request_body_list.append(request_body)
                api_info["request_body"] = json.dumps(request_body_list, ensure_ascii=False)
        except json.JSONDecodeError as e:
            logging.error("处理请求体时发生错误：", exc_info=e)
    api_info["request_data_params"] = json.dumps(request_data_params, ensure_ascii=False)
    api_info["request_header"] = json.dumps(headers, ensure_ascii=False)
    api_info["request_param"] = json.dumps(request_params, ensure_ascii=False)
    return api_info
